#include "common/elements/element_type.hpp"
#include "common/elements/user.h"

#include "common/helpers/container_helper.hpp"

#include "database/data_elements/data_element.h"
#include "database/data_elements/data_element_maker.h"
#include "database/data_elements/data_user.h"

#include "data_controller.h"

DataController::DataController()
{
    loadPermanentElements();
}

bool DataController::loadPermanentElements()
{
    for (auto dataElement : loadElements(Elements::ElementType::User))
        m_users.add(std::static_pointer_cast<Database::DataUser>(dataElement));
    return bool(0 != m_users.size());
}

std::vector<unsigned> DataController::ids(Elements::ElementType elementType) const
{
    switch (elementType)
    {
    case Elements::ElementType::User:
        return m_users.ids();
    default:
        break;
    }
    return {};
}

Database::DataElements DataController::elements(
    Elements::ElementType elementType,
    const std::vector<unsigned> ids
) const
{
    switch (elementType)
    {
    // TODO : Решить вопрос с адекватным кешированием.
    // case Elements::ElementType::User:
    //     if (ids.empty())
    //     {
    //         return m_users.elementsCast<Database::DataElement>();
    //     }
    //     return m_users.elementsCastByPredicate<Database::DataElement>(
    //         [ids](auto user)
    //         {
    //             return Common::contains(ids, user->id());
    //         }
    //     );

    default:
        return loadElements(elementType, ids);
    }
    return {};
}

Database::DataElementPtr DataController::element(
    Elements::ElementType elementType,
    unsigned id
) const
{
    // TODO: Добавить получение элементов из кэша.
    return loadElement(elementType, id);
}

void DataController::save(Elements::ElementPtr element)
{
    save(Database::wrapAnSharedElement(element));
}

void DataController::save(Database::DataElementPtr element)
{
    if (!element)
        return;

    // TODO: Добавить изменение элементов в кэше.
    saveElement(element);
}

void DataController::save(const Database::DataElements& elements)
{
    for (auto element : elements)
        save(element);
}

void DataController::remove(Elements::ElementType type, unsigned id)
{
    remove(type, std::vector<unsigned>{ id });
}

void DataController::remove(Elements::ElementType type, const std::vector<unsigned>& ids)
{
    // TODO: Добавить удаление элементов из кэша.
    Database::removeFromDatabase(*m_database, type, ids);
}

std::vector<std::shared_ptr<Database::DataUser>> DataController::users() const
{
    return m_users.elements();
}

std::shared_ptr<Database::DataUser> DataController::user(unsigned id) const
{
    return m_users.element(id);
}

std::shared_ptr<Database::DataUser> DataController::userByLogin(const std::string& userLogin) const
{
    return m_users.elementByPredicate(
        [userLogin](std::shared_ptr<Database::DataUser> user)
        {
            return userLogin == user->elementCast<Elements::User>()->login();
        }
    );
}

std::vector<std::shared_ptr<Database::DataTeam>> DataController::teams() const
{
    return m_teams.elements();
}

std::shared_ptr<Database::DataTeam> DataController::team(unsigned id) const
{
    return m_teams.element(id);
}

std::vector<std::shared_ptr<Database::DataRole>> DataController::roles() const
{
    return m_roles.elements();
}

std::shared_ptr<Database::DataRole> DataController::role(unsigned id) const
{
    return m_roles.element(id);
}
